package com.codejoys.monitor;

import com.codejoys.monitor.jna.CANLib;
import com.sun.jna.Native;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TextField;
import org.controlsfx.control.StatusBar;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Function;

public class ModbusController implements Initializable {
    private static Logger logger = LoggerFactory.getLogger(ModbusController.class);

    @FXML
    TextField txtNum;

    @FXML
    TextField txtIp;

    Executor executor = Executors.newSingleThreadExecutor();

    @FXML
    StatusBar statusBar;

	@Override
	public void initialize(URL location, ResourceBundle resources) {
	}

    public void onReadStatus(ActionEvent actionEvent) {
        String ip = txtIp.getText();
        String num = txtNum.getText();
        System.out.println("socket address:" + ip + " / num: " + num);

        if (ip.length() == 0 || num.length() == 0) {
            statusBar.setText("请输入ip和端口");
            return;
        }

        executor.execute(() -> {
            Platform.runLater(() -> statusBar.setText("正在打开。。。" + ip));

            try (Socket socket = ensureSocket(ip)) {
                Platform.runLater(() -> statusBar.setText("打开socket成功！"));

                byte[] sendData = {
                        0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x01, // 前7个字节固定
                        0x02,           // 操作码
                        0x00, 0x00,     // 地址
                        0x00, 0x18      // 3号房16，4号房18（表示取数据个数）
                };

                socket.getOutputStream().write(sendData);
                socket.getOutputStream().flush();

                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                logger.info("发送数据成功！等待结果。。。");

                byte[] resultData = new byte[200];
                int read = socket.getInputStream().read(resultData, 0, 200);
                String result = formatBytes(resultData, read);
                logger.debug("响应数据：{}", result);

                // 返回数据示例 00 00 00 00 | 00 06 01 02  | 03 54 AB 02
                // 前8个字节固定，第9个字节3，表示后续3个字节存放结果，低 -> 高
                // resultData[9]:  第10个字节表示0 - 7  号模块开关状态
                // resultData[10]: 第11个字节表示8 - 15 号模块开关状态
                // resultData[11]: 第12个字节表示16 - 17 号模块开关状态（3号房需要，4号房只需要两个字节）

                boolean[] marks = new boolean[18];
                for (int i = 0; i < 8; i++) {
                    int mask = 0x1 << i;
                    marks[i] = ((resultData[9] & mask) == mask);        // 相等表示闭合
                    marks[i + 8] = ((resultData[10] & mask) == mask);
                }

                marks[16] = ((resultData[11] & 0x01) == 0x01);
                marks[17] = ((resultData[11] & 0x02) == 0x02);

                logger.info("开关结果：\n{}", marks);

                System.out.println(Integer.toBinaryString(resultData[9]));
                System.out.println(Integer.toBinaryString(resultData[10]));
                System.out.println(Integer.toBinaryString(resultData[11]));
            } catch (IOException e) {
                e.printStackTrace();
                Platform.runLater(() -> statusBar.setText("打开socket失败！"));
            }
        });
    }

    private static String formatBytes(byte[] bytes, int read) {
        if (read >= bytes.length) {
            read = bytes.length;
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < read; i++) {
            sb.append(String.format("%02X", bytes[i]));
        }

        return sb.toString();
    }

    public Socket ensureSocket(String ip) throws IOException {
        logger.info("开始连接socket。。。");
        Socket socket = new Socket();
        socket.setSoTimeout(2000);  // read timeout, not connect time out
        socket.setKeepAlive(true);
        socket.connect(new InetSocketAddress(ip, Integer.valueOf(502)), 2000);

        logger.info("socket连接成功！");
        return socket;
    }
}
